// tnxel.js
/**
 * 基于ElementPlus的扩展支持
 */
import ElementPlus, {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
import ElementPlus_zh_CN from 'element-plus/es/locale/lang/zh-cn';
import tnxbs from '@truenewx/tnxcore/src/tnxbs'; // 二次封装组件中使用了Bootstrap的基础样式
import tnxvue from '../tnxvue.js';

import Avatar from './avatar/Avatar.vue';
import Alert from './alert/Alert.vue';
import Button from './button/Button.vue';
import CheckIcon from './check-icon/CheckIcon.vue';
import CloseErrorButton from './close-error-button/CloseErrorButton.vue';
import Curd from './curd/Curd.vue';
import DatePicker from './date-picker/DatePicker.vue';
import DateRange from './date-range/DateRange.vue';
import DateTimePicker from './datetime-picker/DateTimePicker.vue';
import DetailForm from './detail-form/DetailForm.vue';
import Dialog from './dialog/Dialog.vue';
import Drawer from './drawer/Drawer.vue';
import DropdownItem from './dropdown-item/DropdownItem.vue';
import EditTable from './edit-table/EditTable.vue';
import EnumSelect from './enum-select/EnumSelect.vue';
import EnumView from './enum-view/EnumView.vue';
import FetchCascader from './fetch-cascader/FetchCascader.vue';
import FetchSelect from './fetch-select/FetchSelect.vue';
import FetchTags from './fetch-tags/FetchTags.vue';
import FssUpload from './fss-upload/FssUpload.vue';
import FssView from './fss-view/FssView.vue';
import Icon from './icon/Icon.vue';
import InputDropdown from './input-dropdown/InputDropdown.vue';
import InputNumber from './input-number/InputNumber.vue';
import Paged from './paged/Paged.vue';
import PermissionTree from './permission-tree/PermissionTree.vue';
import QueryForm from './query-form/QueryForm.vue';
import QueryTable from './query-table/QueryTable.vue';
import RegionCascader from './region-cascader/RegionCascader.vue';
import Select from './select/Select.vue';
import Slider from './slider/Slider.vue';
import StepsNav from './steps-nav/StepsNav.vue';
import SubmitForm from './submit-form/SubmitForm.vue';
import TabColumn from './table-column/TableColumn.vue';
import Tabs from './tabs/Tabs.vue';
import Transfer from './transfer/Transfer.vue';
import Upload from './upload/Upload.vue';

import './tnxel.css';

const $ = tnxbs.libs.$;

export const build = tnxvue.build;

export default build('tnxel', () => {
    const components = Object.assign({}, tnxvue.components, {
        Avatar,
        Alert,
        Button,
        CheckIcon,
        CloseErrorButton,
        Curd,
        DatePicker,
        DateRange,
        DateTimePicker,
        DetailForm,
        Dialog,
        Drawer,
        DropdownItem,
        EditTable,
        EnumSelect,
        EnumView,
        FetchCascader,
        FetchSelect,
        FetchTags,
        FssUpload,
        FssView,
        Icon,
        InputDropdown,
        InputNumber,
        Paged,
        PermissionTree,
        QueryForm,
        QueryTable,
        RegionCascader,
        Select,
        Slider,
        StepsNav,
        SubmitForm,
        TabColumn,
        Tabs,
        Transfer,
        Upload,
    });

    const dialogContainerClass = 'tnxel-dialog-container';
    const drawerContainerClass = 'tnxel-drawer-container';

    const tnxel = Object.assign({}, tnxbs, tnxvue, {
        components,
        componentDefaultApp: undefined, // 组件的默认app，从服务端获取数据的组件以此为远程请求的默认app
        dialogInstances: [], // 对话框堆栈
        dialog(content, title, buttons, options, contentProps) {
            this._closeMessage();

            let componentOptions = {};
            if (window.tnx.util.isComponent(content)) {
                componentOptions.components = {
                    'tnxel-dialog-content': content
                };
                content = null;
            }
            let componentDefinition = Object.assign({}, Dialog, componentOptions);

            const dialogId = 'dialog-' + (new Date().getTime());
            $('body').append('<div class="' + dialogContainerClass + '" id="' + dialogId + '"></div>');
            if (!(buttons instanceof Array)) {
                buttons = [];
            }
            const containerSelector = '.' + dialogContainerClass + '#' + dialogId;
            options = options || {};
            let dialogVm = window.tnx.createVueInstance(componentDefinition, null, {
                modelValue: true,
                container: containerSelector,
                title: title,
                content: content,
                contentProps: contentProps,
                buttons: buttons,
                theme: options.theme,
            });
            const dialog = dialogVm.mount(containerSelector);
            dialog.options = Object.assign(dialog.options || {}, options);
            dialog.options.onClosed = window.tnx.util.function.around(dialog.options.onClosed, function (onClosed) {
                dialogVm.unmount();
                window.tnx.dialogInstances.remove(dialog);
                let $container = $(containerSelector);
                $container.next('.el-overlay').remove();
                $container.remove();
                if (onClosed) {
                    onClosed.call(dialog);
                }
            });
            window.tnx.dialogInstances.push(dialog);
            return dialog;
        },
        closeDialog(all) {
            return new Promise((resolve) => {
                if (window.tnx.dialogInstances.length) {
                    let dialog = window.tnx.dialogInstances.pop();
                    let promises = [];
                    while (dialog) {
                        promises.push(dialog.close());
                        if (all) {
                            dialog = window.tnx.dialogInstances.pop();
                        } else {
                            break;
                        }
                    }
                    Promise.all(promises).then(resolve);
                } else {
                    resolve();
                }
            });
        },
        drawerInstances: [], // 抽屉堆栈
        drawer(content, title, buttons, options, contentProps) {
            this._closeMessage();

            let componentOptions = {};
            if (this.util.isComponent(content)) {
                componentOptions.components = {
                    'tnxel-drawer-content': content
                };
                content = null;
            }
            let componentDefinition = Object.assign({}, Drawer, componentOptions);

            const drawerId = 'drawer-' + (new Date().getTime());
            $('body').append('<div class="' + drawerContainerClass + '" id="' + drawerId + '"></div>');
            if (!(buttons instanceof Array)) {
                buttons = [];
            }
            const containerSelector = '.' + drawerContainerClass + '#' + drawerId;
            options = options || {};
            let drawerVm = window.tnx.createVueInstance(componentDefinition, null, {
                content: content,
                title: title,
                contentProps: contentProps,
                buttons: buttons,
                theme: options.theme,
            });
            const drawer = drawerVm.mount(containerSelector);
            drawer.id = drawerId;
            drawer.options = Object.assign(drawer.options || {}, options);
            drawer.options.onClosed = this.util.function.around(drawer.options.onClosed, function (onClosed) {
                drawerVm.unmount();
                window.tnx.drawerInstances.remove(drawer);
                let $container = $(containerSelector);
                $container.next('.el-overlay').remove();
                $container.remove();
                if (onClosed) {
                    onClosed.call(drawer);
                }
            });
            window.tnx.drawerInstances.push(drawer);
            return drawer;
        },
        closeDrawer(all) {
            return new Promise((resolve) => {
                if (window.tnx.drawerInstances.length) {
                    let drawer = window.tnx.drawerInstances.pop();
                    let promises = [];
                    while (drawer) {
                        promises.push(drawer.close());
                        if (all) {
                            drawer = window.tnx.drawerInstances.pop();
                        } else {
                            break;
                        }
                    }
                    Promise.all(promises).then(resolve);
                } else {
                    resolve();
                }
            });
        },
        _closeMessage() {
            ElMessage.closeAll();
            this.closeLoading();
        },
        _handleZIndex(selector) {
            setTimeout(() => {
                const topZIndex = window.tnx.util.dom.minTopZIndex(2);
                if (selector.endsWith(':last')) {
                    selector = selector.substring(0, selector.length - ':last'.length);
                }
                let element = $(selector);
                if (element.length > 0) {
                    element = $(element[element.length - 1]);
                }
                const zIndex = Number(element.css('zIndex'));
                if (isNaN(zIndex) || topZIndex > zIndex) {
                    element.css('zIndex', topZIndex);
                    const modal = element.next();
                    if (modal.is('.v-modal')) {
                        modal.css('zIndex', topZIndex - 1);
                    }
                }
            });
        },
        alert(message, title, options) {
            if (typeof title === 'object') {
                options = title;
                title = '提示';
            }
            options = Object.assign({
                dangerouslyUseHTMLString: true,
                type: 'warning',
                confirmButtonText: '确定',
            }, options);
            this._closeMessage();
            const promise = ElMessageBox.alert(message, title || '提示', options);
            this._handleZIndex('.el-message-box__wrapper:last');
            this.app.eventBus.emit('tnx.alert', options);
            return promise;
        },
        success(message, options) {
            options = Object.assign({
                dangerouslyUseHTMLString: true,
            }, options, {
                type: 'success',
                confirmButtonText: '确定',
            });
            this._closeMessage();
            const promise = ElMessageBox.alert(message, '成功', options);
            this._handleZIndex('.el-message-box__wrapper:last');
            this.app.eventBus.emit('tnx.success', options);
            return promise;
        },
        error(message, options) {
            options = Object.assign({
                dangerouslyUseHTMLString: true,
            }, options, {
                type: 'error',
                confirmButtonText: '确定',
            });
            this._closeMessage();
            const promise = ElMessageBox.alert(message, '错误', options);
            this._handleZIndex('.el-message-box__wrapper:last');
            this.app.eventBus.emit('tnx.error', options);
            // 打印TypeError对象，便于调试
            if (typeof message === 'object' && message.name === 'TypeError') {
                console.error(message);
            }
            return promise;
        },
        confirm(message, title = '确认', options) {
            if (typeof title === 'object') {
                options = title;
                title = '确认';
            }
            options = Object.assign({
                type: 'question',
                confirmButtonText: '确定',
                cancelButtonText: '取消',
            }, options, {
                dangerouslyUseHTMLString: true,
                distinguishCancelAndClose: true,
            });
            if (options.type === 'question') {
                options.type = 'info';
                options.icon = tnxvue.libs.Vue.markRaw(Icon.components.QuestionFilled);
            }
            if (options.reverse) {
                options.customClass = 'reverse';
                let buttonText = options.confirmButtonText;
                options.confirmButtonText = options.cancelButtonText;
                options.cancelButtonText = buttonText;
            }

            this._closeMessage();
            return new Promise(resolve => {
                ElMessageBox.confirm(message, title, options).then(() => {
                    resolve(true);
                }).catch(() => {
                    resolve(false);
                });
                this._handleZIndex('.el-message-box__wrapper:last');
                this.app.eventBus.emit('tnx.confirm', options);
            })

        },
        toast(message, timeout, options) {
            options = Object.assign({
                type: 'success', // 默认为成功主题，可更改为其它主题
                offset: this.util.dom.getDocHeight() * 0.4,
                dangerouslyUseHTMLString: true,
            }, options, {
                center: true, // 因为是竖向排列，所以必须居中
                showClose: false,
                message: message,
                duration: timeout || 1500,
                customClass: 'tnxel-toast',
            });

            this._closeMessage();
            const messageInstance = ElMessage(options);
            this._handleZIndex('.el-message:last');
            this.app.eventBus.emit('tnx.toast', options);

            // 创建一个Promise，在消息关闭时resolve
            return new Promise((resolve) => {
                const originalOnClose = options.onClose;
                options.onClose = () => {
                    if (originalOnClose) {
                        originalOnClose();
                    }
                    resolve();
                };
                // 如果消息实例有close方法，重新设置onClose
                if (messageInstance && messageInstance.close) {
                    const originalClose = messageInstance.close;
                    messageInstance.close = () => {
                        originalClose.call(messageInstance);
                        options.onClose();
                    };
                }
            });
        },
        showLoading(message, options) {
            if (typeof message !== 'string') {
                options = message;
                message = undefined;
            }
            options = Object.assign({
                dangerouslyUseHTMLString: true,
            }, options, {
                text: message,
            });
            return new Promise((resolve, reject) => {
                window.tnx._closeMessage();
                try {
                    window.tnx.loadingInstance = ElLoading.service(options);
                    this._handleZIndex('.el-loading-mask');
                    window.tnx.app.eventBus.emit('tnx.showLoading', options);
                    let vm = window.tnx.loadingInstance.vm;
                    if (vm) {
                        window.tnx.nextTickTimeout(vm, () => {
                            resolve(window.tnx.loadingInstance);
                        }, 500);
                    } else {
                        resolve(window.tnx.loadingInstance);
                    }
                } catch (e) {
                    window.tnx.loadingInstance = null;
                    console.error(e);
                    reject(e);
                }
            });
        },
        closeLoading() {
            if (window.tnx.loadingInstance) { // 确保绝对的单例
                window.tnx.loadingInstance.close();
                window.tnx.loadingInstance = undefined;
            }
        },
        hideLoading() {
            this.closeLoading();
        },
        validateUploaded(vm, reject) {
            let result = true;
            let formRef = null;
            let refKeys = Object.keys(vm.$refs);
            for (let refKey of refKeys) {
                let refObj = vm.$refs[refKey];
                if (Array.isArray(refObj)) {
                    for (let ref of refObj) {
                        if (typeof ref.validateUploaded === 'function') {
                            if (ref.validateUploaded(reject) === false) {
                                result = false;
                                break;
                            }
                        }
                    }
                } else if (refObj.$el.tagName === 'FORM' && typeof refObj.disable === 'function') {
                    formRef = refObj;
                } else {
                    if (typeof refObj.validateUploaded === 'function') {
                        if (refObj.validateUploaded(reject) === false) {
                            result = false;
                            break;
                        }
                    }
                }
            }
            if (!result && formRef) {
                formRef.disable(false);
            }
            return result;
        },
    });

    tnxel.libs.ElementPlus = ElementPlus;

    tnxel.install = tnxel.util.function.around(tnxel.install, function (install, vm) {
        install.call(tnxel, vm);
        // 始终安装ElementPlus，以避免对于不同Vue实例未安装的问题
        vm.use(ElementPlus, {
            locale: ElementPlus_zh_CN,
        });
    });

    tnxel.router.beforeLeave =
        tnxel.util.function.around(tnxel.router.beforeLeave, function (beforeLeave, router, from) {
            // 页面跳转前关闭当前页面中可能存在的所有消息框和对话框
            window.tnx._closeMessage();
            window.tnx.closeDialog(true);
            beforeLeave.call(window.tnx.router, router, from);
        });

    tnxel.date = {
        formatDateTime: function (row, column, cellValue) {
            if (cellValue) {
                return new Date(cellValue).formatDateTime();
            }
            return undefined;
        },
        formatDate: function (row, column, cellValue) {
            if (cellValue) {
                return new Date(cellValue).formatDate();
            }
            return undefined;
        },
        formatTime: function (row, column, cellValue) {
            if (typeof cellValue === 'number') {
                cellValue = new Date(cellValue);
            }
            if (cellValue instanceof Date) {
                cellValue = cellValue.formatTime();
            }
            if (typeof cellValue === 'string') {
                return cellValue;
            }
            return undefined;
        },
        formatTimeMinute: function (row, column, cellValue) {
            if (typeof cellValue === 'number') {
                cellValue = new Date(cellValue);
            }
            if (cellValue instanceof Date) {
                cellValue = cellValue.formatTimeMinute();
            }
            if (typeof cellValue === 'string') {
                let array = cellValue.split(':');
                if (array.length > 1) {
                    return array[0] + ':' + array[1];
                }
            }
            return undefined;
        },
        formatDateMinute: function (row, column, cellValue) {
            if (cellValue) {
                return new Date(cellValue).formatDateMinute();
            }
            return undefined;
        },
        formatDateMonth: function (row, column, cellValue) {
            if (cellValue) {
                return new Date(cellValue).formatDateMonth();
            }
            return undefined;
        },
        formatPermanentableDate: function (row, column, cellValue) {
            if (Array.isArray(cellValue)) {
                cellValue = cellValue[column];
            }
            return tnxvue.util.date.formatPermanentableDate(cellValue);
        },
        /**
         * 将Java标准的日期格式转换为Day.js的日期格式
         * @param format Java标准的日期格式
         * @returns {String} Day.js的日期格式
         */
        toDayJsDateFormat(format) {
            return format.replaceAll('y', 'Y').replaceAll('d', 'D');
        },
    };

    tnxel.number = {
        formatPercent: function (row, column, cellValue) {
            if (typeof cellValue !== 'number') {
                cellValue = parseFloat(cellValue);
            }
            if (!isNaN(cellValue)) {
                return cellValue.toPercent();
            }
            return undefined;
        }
    }

    tnxel.boolean = {
        items: {
            getText(type, value) {
                let items = this[type];
                if (Array.isArray(items)) {
                    for (let item of items) {
                        if (item.value === value) {
                            return item.text;
                        }
                    }
                }
                return undefined;
            },
            has: [{
                value: true,
                text: '有',
            }, {
                value: false,
                text: '无',
            }]
        },
        format: function (row, column, cellValue) {
            if (typeof cellValue === 'boolean') {
                cellValue = cellValue.toText();
            }
            return cellValue;
        },
        formatHas: function (row, column, cellValue) {
            if (typeof cellValue === 'boolean') {
                cellValue = tnxel.boolean.items.getText('has', cellValue);
            }
            return cellValue;
        }
    }

    tnxel.table = {}

    const rpc = tnxel.app.rpc;
    rpc.handleErrors = tnxel.util.function.around(rpc.handleErrors, function (handleErrors, errors, options) {
        if (options && options.form) {
            let forms;
            if (Array.isArray(options.form)) {
                forms = options.form;
            } else {
                forms = [options.form];
            }
            forms.forEach(form => {
                if (typeof form.disable === 'function') {
                    form.disable(false);
                }
            });
        }
        return handleErrors.call(rpc, errors, options);
    });
    return tnxel;
});
